package com.demo.steammanagement.controller;


import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.demo.steammanagement.common.DateUtils;
import com.demo.steammanagement.common.ResultMsg;
import com.demo.steammanagement.config.response.BizException;
import com.demo.steammanagement.model.entity.*;
import com.demo.steammanagement.model.vo.*;
import com.demo.steammanagement.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.ArrayList;

@RestController
@Slf4j
public class ComController {

  @Autowired
  private IUsersService userService;
  @Autowired
  private IRentalsService rentalsService;
  @Autowired
  private IGamesService gameService;
  @Autowired
  private IGameAccountsService gameAccountsService;
  @Autowired
  private RedeemCodeService redeemCodeService;

  // 使用ConcurrentHashMap存储每个兑换码对应的锁对象
  private final ConcurrentHashMap<String, Lock> redeemCodeLocks = new ConcurrentHashMap<>();

  @PostMapping("/com/updatePwd")
  public ResponseEntity updatePwd(@RequestParam("password") String pwd) {
    userService.changePassword(pwd);
    return ResponseEntity.ok("successfully");
  }

  @GetMapping("/com/checkLogin")
  public Result checkLogin() {
    String username = SecurityContextHolder.getContext().getAuthentication().getName();
    if ("anonymousUser".equals(username)) {
      return Result.fail(403, "not login");
    }
    Users userByUsername = userService.getUserByUsername(username);
    return Result.success(userByUsername.getAccount());
  }

  @GetMapping("/com/getUserInfo")
  public Result getUserByUsername() {
    String username = SecurityContextHolder.getContext().getAuthentication().getName();
    if ("anonymousUser".equals(username)) {
      return Result.fail(403, "not login");
    }
    Users user = userService.getUserByUsername(username);
    if (user != null) {
      user.setPassword(null);
    }
    return Result.success(user);
  }

  // 获取当前用户租用记录
  @GetMapping("/com/rentals/page")
  public IPage<RentalVO> page(RentalVO vo) {
    if (ObjectUtils.isEmpty(vo)) {
      throw new BizException(ResultMsg.OBJECT_IS_EMPTY.getMsg());
    }
    String username = SecurityContextHolder.getContext().getAuthentication().getName();
    Users user = userService.getUserByUsername(username);
    Page<Rentals> page = new Page<>(vo.getPageNo(), vo.getPageSize());
    QueryWrapper<Rentals> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda()
            .eq(ObjectUtils.isNotEmpty(user.getId()), Rentals::getUserId, user.getId())
            .eq(ObjectUtils.isNotEmpty(vo.getStatus()), Rentals::getStatus, vo.getStatus());
    Page<Rentals> page1 = rentalsService.page(page, queryWrapper);
    // 将每条租赁记录转换为RentalWithGameVO，并附加游戏信息
    List<RentalVO> rentalWithGameList = page1.getRecords().stream()
            .map(rental -> {
              RentalVO voItem = new RentalVO();

              // 查询并设置游戏信息
              Games game = gameService.getById(rental.getGameId());
              GameAccounts gameAccounts = gameAccountsService.getById(rental.getGameAccountId());
              voItem.setGameId(game.getId());
              voItem.setImageUrl(game.getImageUrl());
              voItem.setName(game.getName());
              voItem.setPassword(gameAccounts != null ? gameAccounts.getPassword() : "-");
              // 游戏账号信息
              voItem.setGameAccountId(rental.getGameAccountId());
              // 设置租赁信息
              voItem.setId(rental.getId());
              voItem.setAccount(rental.getAccount());
              voItem.setStartTime(rental.getStartTime());
              voItem.setEndTime(rental.getEndTime());
              voItem.setStatus(rental.getStatus());
              voItem.setCreatedAt(rental.getCreatedAt());
              voItem.setLastExtendTime(rental.getLastExtendTime());
              return voItem;
            })
            .collect(Collectors.toList());

    // 创建一个新的IPage实例来保存转换后的数据
    IPage<RentalVO> resultPage = new Page<>();
    resultPage.setTotal(page1.getTotal());
    resultPage.setSize(page1.getSize());
    resultPage.setCurrent(page1.getCurrent());
    resultPage.setRecords(rentalWithGameList);
    return resultPage;
  }

  @PostMapping("/com/rentals/stop")
  @Transactional
  public Result stop(RentalVO vo) {
    if (ObjectUtils.isEmpty(vo)) {
      throw new BizException(ResultMsg.OBJECT_IS_EMPTY.getMsg());
    }
    String username = SecurityContextHolder.getContext().getAuthentication().getName();
    Users user = userService.getUserByUsername(username);
    QueryWrapper<Rentals> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda()
            .eq(ObjectUtils.isNotEmpty(user.getId()), Rentals::getUserId, user.getId())
            .eq(ObjectUtils.isNotEmpty(vo.getId()), Rentals::getId, vo.getId());
    Rentals rentals = rentalsService.getOne(queryWrapper);
    if (rentals == null) {
      throw new BizException(ResultMsg.OBJECT_IS_EMPTY.getMsg());
    }
    //请注意租号后需要游玩30分钟才可以更换其他游戏或账号
    // 这里是最少有一个记录为租号中 获取用户正在租号的记录
    Rentals one = rentalsService.getOne(new LambdaQueryWrapper<Rentals>()
            .eq(Rentals::getUserId, user.getId())
            .eq(Rentals::getStatus, 1)
    );
    //判断建立时间是否符合规则 是否过30分钟
    if (ObjectUtils.isNotEmpty(one)) {
      Date now = new Date();
      // 计算时间差（分钟）
      long between = (now.getTime() - one.getCreatedAt().getTime()) / (1000 * 60);

      if (between < 30) {
        throw new BizException("请注意租号后需要游玩30分钟才可以更换其他游戏或账号");
      }
    }

    rentals.setStatus(0);
    // 修该自己的租用记录
    rentalsService.updateById(rentals);
    // 修改对应账号的状态
    gameAccountsService.update(new LambdaUpdateWrapper<GameAccounts>()
            .set(GameAccounts::getStatus, 0)
            .eq(GameAccounts::getId, rentals.getGameAccountId()));
    return Result.success();
  }

  // 租用记录延长
  @PostMapping("/com/rentals/extend")
  public Result extend(@RequestBody RentalVO vo) {
    if (ObjectUtils.isEmpty(vo)) {
      throw new BizException(ResultMsg.OBJECT_IS_EMPTY.getMsg());
    }
    String username = SecurityContextHolder.getContext().getAuthentication().getName();
    Users user = userService.getUserByUsername(username);
    QueryWrapper<Rentals> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda()
            .eq(ObjectUtils.isNotEmpty(user.getId()), Rentals::getUserId, user.getId())
            .eq(ObjectUtils.isNotEmpty(vo.getId()), Rentals::getId, vo.getId());
    Rentals rentals = rentalsService.getOne(queryWrapper);// 是否是自己的记录

    if (rentals == null) {
      return Result.fail(404, "租用记录不存在");
    }

    // 检查是否可以延长
    Date now = new Date();
    if (rentals.getEndTime().before(now)) {
      return Result.fail(404, "租用已过期，无法延长");
    }

    // 检查上次延长时间
    if (rentals.getLastExtendTime() != null) {
      long hoursSinceLastExtend = (now.getTime() - rentals.getLastExtendTime().getTime()) / (1000 * 60 * 60);
      if (hoursSinceLastExtend < 24) {
        return Result.fail(404, "距离上次延长不足24小时，请稍后再试");
      }
    }

    // 延长24小时
    LocalDateTime endDateTime = rentals.getEndTime().toInstant()
            .atZone(ZoneId.systemDefault())
            .toLocalDateTime()
            .plusHours(24);
    rentals.setEndTime(Date.from(endDateTime.atZone(ZoneId.systemDefault()).toInstant()));
    rentals.setLastExtendTime(now);
    rentalsService.updateById(rentals);
    return Result.success();
  }

  @GetMapping("/com/rentals/getVerifi/{gameAccountId}/{rentalId}")
  public Result getRentals(@PathVariable("gameAccountId") Long gameAccountId) {
    if (gameAccountId == null) {
      throw new BizException(ResultMsg.OBJECT_IS_EMPTY.getMsg());
    }
    String username = SecurityContextHolder.getContext().getAuthentication().getName();
    Users user = userService.getUserByUsername(username);
    Rentals one = rentalsService.getOne(new LambdaQueryWrapper<Rentals>()
            .eq(Rentals::getGameAccountId, gameAccountId)
            .eq(Rentals::getStatus, 1)
            .eq(Rentals::getUserId, user.getId())
    );
    if (one == null) {
      throw new BizException(ResultMsg.DATA_IS_NULL.getMsg());
    }
    // 根据游戏账号id 获取游戏账号信息
    GameAccounts gameAccounts = gameAccountsService.getById(gameAccountId);
    // 使用hutool 工具包 发送get请求
    String url = "http://47.98.115.217:8080/steam_code/api/user/codeGenerator?maFile=" + URLEncoder.encode(gameAccounts.getStoken(), StandardCharsets.UTF_8);
    String result = HttpUtil.get(url);
    return Result.success(result);
  }

  @GetMapping("/com/gameAccount/page")
  public IPage<GameAccounts> page(GameAccountVO vo) {
    if (ObjectUtils.isEmpty(vo)) {
      throw new BizException(ResultMsg.OBJECT_IS_EMPTY.getMsg());
    }
    Page<GameAccounts> page = new Page<>(vo.getPageNo(), vo.getPageSize());
    QueryWrapper<GameAccounts> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda()
            .select(GameAccounts.class, info -> !info.getColumn().equals("stoken"))
            .like(StringUtils.isNotBlank(vo.getGameAccount()), GameAccounts::getGameAccount, vo.getGameAccount())
            .eq(ObjectUtils.isNotEmpty(vo.getGameId()), GameAccounts::getGameId, vo.getGameId())
            .eq(ObjectUtils.isNotEmpty(vo.getStatus()), GameAccounts::getStatus, vo.getStatus());
    return gameAccountsService.page(page, queryWrapper);
  }

  @GetMapping("com/game/page")
  public IPage<Games> page(GamesVO vo) {

    if (ObjectUtils.isEmpty(vo)) {
      throw new BizException(ResultMsg.OBJECT_IS_EMPTY.getMsg());
    }

    Page<Games> page = new Page<>(vo.getPageNo(), vo.getPageSize());
    QueryWrapper<Games> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda()
            .like(StringUtils.isNotBlank(vo.getName()), Games::getName, vo.getName())
            .orderByDesc(Games::getCreatedAt)
            .eq(StringUtils.isNotBlank(vo.getCategory()), Games::getCategory, vo.getCategory());
    return gameService.page(page, queryWrapper);

  }


  @PostMapping("com/redeemCode/redeem")
  @Transactional
  public Result redeem(@RequestBody RedeemCodeInfoVO vo) throws IOException {
    String redeemCode = vo.getRedeemCode();

    // 获取或创建兑换码的锁对象
    Lock lock = redeemCodeLocks.computeIfAbsent(redeemCode, k -> new ReentrantLock());

    if (lock.tryLock()) {
      try {
        RedeemCodeInfo codeInfo = redeemCodeService.getRedeemCodeByCode(redeemCode);
        if (codeInfo == null) {
          return Result.fail(400, "兑换码不存在");
        } else if (RedeemCodeInfo.STATUS_ALREADYREDEEMED.equals(codeInfo.getStatus())) {
          return Result.fail(400, "兑换失败,兑换码己兑换");
        }

        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        Users user = userService.getUserByUsername(username);

        Integer userRole = Integer.parseInt(user.getUserRole());


        //兑换码是高级，会员是普通并且未过期，优先使用高级
        if ((codeInfo.getUserRole() == 2 && userRole == 3) && !DateUtils.isLessThanCurrentTime(user.getAccountExpirationDate())) {
          user.setUserRole(String.valueOf(4));
          user.setNormalValidity(DateUtils.between(user.getAccountExpirationDate()));
          user.setAccountExpirationDate(DateUtils.dataPlus(user.getAccountExpirationDate(), codeInfo.getValidity(), true));

          // 兑换码是普通,会员是普通的并且未过期 或 兑换码是高级,会员是高级的并且未过期，时间叠加
        } else if (((codeInfo.getUserRole() == 1 && userRole == 3) || (codeInfo.getUserRole() == 2 && userRole == 4))
                && !DateUtils.isLessThanCurrentTime(user.getAccountExpirationDate())) {
          user.setAccountExpirationDate(DateUtils.dataPlus(user.getAccountExpirationDate(), codeInfo.getValidity(), false));

          // 兑换码是普通,会员是普通的并且已过期 或 兑换码是高级,会员是高级的并且已过期，从当前时间开始计算
        } else if (((codeInfo.getUserRole() == 1 && userRole == 3) || (codeInfo.getUserRole() == 2 && userRole == 4))
                && DateUtils.isLessThanCurrentTime(user.getAccountExpirationDate())) {
          user.setAccountExpirationDate(DateUtils.dataPlus(user.getAccountExpirationDate(), codeInfo.getValidity(), true));

          // 兑换码是普通的，会员是高级并且已过期，从当前时间开始计算并降级普通会员
        } else if (codeInfo.getUserRole() == 1 && userRole == 4 && DateUtils.isLessThanCurrentTime(user.getAccountExpirationDate())) {
          user.setUserRole(String.valueOf(3));
          user.setAccountExpirationDate(DateUtils.dataPlus(user.getAccountExpirationDate(), codeInfo.getValidity(), true));

          // 会员是高级的并且未过期，兑换码是普通的，增加普通会员时间
        } else if (userRole == 4 && codeInfo.getUserRole() == 1 && !DateUtils.isLessThanCurrentTime(user.getAccountExpirationDate())) {
          if (user.getNormalValidity() > 0) {
            user.setNormalValidity(user.getNormalValidity() + codeInfo.getValidity());
          } else {
            user.setNormalValidity(codeInfo.getValidity());
          }

          // 普通用户
        } else if (userRole == 2) {
          user.setUserRole(codeInfo.getUserRole() == 1 ? String.valueOf(3) : String.valueOf(4));
          user.setAccountExpirationDate(DateUtils.dataPlus(user.getAccountExpirationDate(), codeInfo.getValidity(), true));
        }

        userService.updateById(user);

        // 更新用户角色后刷新权限
        List<GrantedAuthority> updatedAuthorities = new ArrayList<>();
        if ("1".equals(user.getUserRole())) {
          updatedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        } else if ("2".equals(user.getUserRole())) {
          updatedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER2"));
        } else if ("3".equals(user.getUserRole())) {
          updatedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER3"));
        } else if ("4".equals(user.getUserRole())) {
          updatedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER4"));
        }
        log.info("更新用户角色后刷新权限");

        UserDetails updatedUserDetails = new User(user.getAccount(), user.getPassword(), updatedAuthorities);
        SecurityContextHolder.getContext().setAuthentication(
                new UsernamePasswordAuthenticationToken(updatedUserDetails, null, updatedAuthorities)
        );

        codeInfo.setStatus(RedeemCodeInfo.STATUS_ALREADYREDEEMED);
        codeInfo.setUserName(user.getAccount());
        redeemCodeService.updateById(codeInfo);

        return Result.success();
      } finally {
        lock.unlock();
      }
    } else {
      return Result.fail(400, "兑换码正在被其他用户兑换，请稍后再试");
    }
  }
}
